package weightmanager;

import java.io.IOException;
import java.net.UnknownHostException;
import java.sql.Date;
import java.util.List;

import database.DALException;
import database.MySQLOperatoerDAO;
import database.MySQLProduktBatchDAO;
import database.MySQLProduktBatchKompDAO;
import database.MySQLRaavareBatchDAO;
import database.MySQLRaavareDAO;
import database.MySQLReceptDAO;
import database.MySQLReceptKompDAO;
import database.OperatoerDAO;
import database.OperatoerDTO;
import database.ProduktBatchDAO;
import database.ProduktBatchDTO;
import database.ProduktBatchKompDAO;
import database.ProduktBatchKompDTO;
import database.RaavareBatchDAO;
import database.RaavareBatchDTO;
import database.RaavareDAO;
import database.ReceptDAO;
import database.ReceptDTO;
import database.ReceptKompDAO;
import database.ReceptKompDTO;

/**
 * 
 * @author Lasse Poulsen
 * 
 */
public class Client extends Thread {

	private static final int STATE_NEW = 0;
	private static final int STATE_LOGGEDOUT = 1;
	private static final int STATE_LOGIN_USER = 2;
	private static final int STATE_LOGIN_PASS = 3;
	private static final int STATE_LOGIN_CHECK = 4;
	private static final int STATE_RECEPT_WEIGH = 5;
	private static final int STATE_RECEPT_SELECT_NUMBER = 6;
	private static final int STATE_RECEPT_VERIFY = 7;
	private static final int STATE_DB_ERROR = 8;
	private static final int STATE_RECEPT_BATCH_NON_EXISTS = 9;

	public static final int DIALOG_NO_BUTTONS = 1;
	public static final int DIALOG_OK = 2;
	public static final int DIALOG_CANCEL = 3;
	public static final int DIALOG_OK_CANCEL = 4;

	private static final int PRODUCT_BATCH_STATUS_STARTED = 2;
	private static final int PRODUCT_BATCH_STATUS_FINISHED = 1;

	private TCPClient tcp;
	private int state;
	private boolean running = true;
	private String name;
	private String pass;
	private int prodBatchNumber;
	private RaavareDAO raavareDAO;
	private ReceptDAO receptDAO;
	private ReceptKompDAO receptKompDAO;
	private OperatoerDAO userDAO;
	private ReceptDTO receptDTO;
	private ProduktBatchDAO produktBatchDAO;
	private ProduktBatchDTO produktBatchDTO;
	private RaavareBatchDAO raavareBatchDAO;
	private ProduktBatchKompDAO produktBatchKompDAO;
	private OperatoerDTO user;

	public Client(String host, int port) throws UnknownHostException,
			IOException, DALException {
		tcp = new TCPClient(host, port);

		/*
		 * TODO import database.RaavareBatchDAO;
		 */

		receptDAO = new MySQLReceptDAO();
		receptKompDAO = new MySQLReceptKompDAO();
		userDAO = new MySQLOperatoerDAO();
		raavareDAO = new MySQLRaavareDAO();
		produktBatchDAO = new MySQLProduktBatchDAO();
		raavareBatchDAO = new MySQLRaavareBatchDAO();
		produktBatchKompDAO = new MySQLProduktBatchKompDAO();
	}

	public void run() {
		while (running) {
			switch (state) {
			case STATE_NEW:
				System.err.println("STATE_NEW");
				// Reset the server
				produktBatchDTO = null;
				receptDTO = null;
				user = null;
				reset();
				state = STATE_LOGGEDOUT;
				break;
			case STATE_LOGGEDOUT:
				System.err.println("STATE_LOGGEDOUT");
				String menues[] = new String[1];
				menues[0] = "Login";
				int choice = menu(menues);

				System.out.println("Login menu choice: " + choice);
				if (choice == 1)
					state = STATE_LOGIN_USER;
				else
					state = STATE_NEW;
				clearMenu();
				break;
			case STATE_LOGIN_USER:
				System.err.println("STATE_LOGIN_USER");
				if (!askUsername()) {
					state = STATE_NEW;
					break;
				}
				state = STATE_LOGIN_PASS;
				break;
			case STATE_LOGIN_PASS:
				System.err.println("STATE_LOGIN_PASS");
				if (!askPassword()) {
					state = STATE_NEW;
					break;
				}
				state = STATE_LOGIN_CHECK;
				break;
			case STATE_LOGIN_CHECK:
				System.err.println("STATE_LOGIN_CHECK");
				if (checkCredentials())
					state = STATE_RECEPT_SELECT_NUMBER;
				else {
					name = null;
					pass = null;
					state = STATE_NEW;
				}
				break;
			case STATE_RECEPT_SELECT_NUMBER:
				System.err.println("STATE_RECEPT_SELECT_NUMBER");
				prodBatchNumber = getNumber("Enter prod. batch no.");
				if (prodBatchNumber == -1) {
					prodBatchNumber = 0;
					state = STATE_NEW;
					break;
				}
				state = STATE_RECEPT_VERIFY;
				break;
			case STATE_RECEPT_VERIFY:
				System.err.println("STATE_RECEPT_VERIFY");
				try {
					produktBatchDTO = produktBatchDAO
							.getProduktBatch(prodBatchNumber);
					receptDTO = receptDAO.getRecept(produktBatchDTO
							.getReceptId());
				} catch (DALException e) {
					state = STATE_RECEPT_BATCH_NON_EXISTS;
					break;
				}
				if (receptDTO == null) {
					state = STATE_RECEPT_SELECT_NUMBER;
					break;
				}
				if (dialog("You have chosen " + receptDTO.getReceptNavn()
						+ " is this correct?", DIALOG_OK_CANCEL)) {
					try {
						produktBatchDTO.setStatus(PRODUCT_BATCH_STATUS_STARTED);
						java.util.Date now = new java.util.Date();
						produktBatchDTO.setStartDato(new Date(now.getTime()));
						produktBatchDAO.updateProduktBatch(produktBatchDTO);
					} catch (DALException e) {
						dialog("Error starting batch, error in the database.",
								DIALOG_OK);
						state = STATE_RECEPT_SELECT_NUMBER;
						break;
					}
					state = STATE_RECEPT_WEIGH;
				} else {
					state = STATE_RECEPT_SELECT_NUMBER;
				}
				break;
			case STATE_RECEPT_WEIGH:
				System.err.println("STATE_RECEPT_WEIGH");
				List<ReceptKompDTO> receptkomplist = null;
				try {
					
					receptkomplist = receptKompDAO
							.getReceptKompList(produktBatchDTO.getReceptId());
				} catch (DALException e) {
					e.printStackTrace();
					state = STATE_DB_ERROR;
					break;
				}

				if (receptkomplist == null) {
					dialog("This recept does not contain anything!",
							DIALOG_CANCEL);
					state = STATE_NEW;
					break;
				}

				wd();
				for (int i = 0; i < receptkomplist.size(); i++) {
					weigh(receptkomplist.get(i).getRaavareId(), receptkomplist
							.get(i).getNomNetto(), receptkomplist.get(i)
							.getTolerance());
					
				}

				try {
					produktBatchDTO = produktBatchDAO
							.getProduktBatch(prodBatchNumber);
					receptDTO = receptDAO.getRecept(produktBatchDTO
							.getReceptId());
					produktBatchDTO.setStatus(PRODUCT_BATCH_STATUS_FINISHED);
					java.util.Date now = new java.util.Date();
					produktBatchDTO.setStartDato(new Date(now.getTime()));
					produktBatchDAO.updateProduktBatch(produktBatchDTO);
				} catch (DALException e) {
				}

				wdClear();
				state = STATE_NEW;
				break;
			case STATE_DB_ERROR:
				System.err.println("STATE_DB_ERROR");
				dialog("Communication error, could not talk to the database.",
						DIALOG_OK);
				state = STATE_NEW;
				break;
			case STATE_RECEPT_BATCH_NON_EXISTS:
				System.err.println("STATE_RECEPT_BATCH_NON_EXISTS");
				dialog("Recept batch number does not exist.", DIALOG_OK);
				state = STATE_RECEPT_SELECT_NUMBER;
			}
		}
	}

	private void reset() {
		tcp.sendLine("@");
		tcp.sendLine("P120");
		tcp.sendLine("P124");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(3500);
			} catch (InterruptedException e) {
			}
		}
		while (tcp.getLineAvalible())
			tcp.getLine();

		wdClear();
	}

	private void wd() {
		tcp.sendLine("DW");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		tcp.getLine();
	}

	private void wdClear() {
		tcp.sendLine("D \"\"");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		tcp.getLine();
	}

	private void clearMenu() {
		tcp.sendLine("RM39 0");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		tcp.getLine();
	}

	private int getNumber(String text) {
		tcp.sendLine("RM20 3 \"" + text.replaceAll("\"", "") + "\" \"\" \"\"");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		String line = tcp.getLine();
		if (line.equals("RM20 B")) {
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			String noline = tcp.getLine();
			if (noline.contains("RM20 A ")) {
				if (noline.equals("RM20 A \"\""))
					return 0;
				noline = noline.replaceAll("RM20 A ", "").replaceAll("\"", "");
				return Integer.parseInt(noline);
			}
			if (noline.equals("RM20 C"))
				return -1;
		}
		return 0;
	}

	private boolean checkCredentials() {
		if ((name == null) || (name.length() > 1)) {
			dialog("Username and/or password incorrect\"", DIALOG_OK);
			return false;
		}

		user = userDAO.getOperatoer(name);
		if ((user != null) && (user.getKodeord().contains(pass))) {
			if (dialog("You are now logged in as \"" + name + "\"",
					DIALOG_OK_CANCEL))
				return true;
			else
				return false;
		} else {
			dialog("Username and/or password incorrect\"", DIALOG_OK);
			return false;
		}
	}

	private boolean askPassword() {
		tcp.sendLine("RM20 11 \"Password: \" \"\" \"\"");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		if (tcp.getLine().equals("RM20 B")) {
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			String passline = tcp.getLine();
			if (passline.contains("RM20 A ")) {
				passline = passline.replaceAll("RM20 A ", "").replaceAll("\"",
						"");
				pass = passline;
				return true;
			}
		}
		return false;
	}

	private boolean askUsername() {
		tcp.sendLine("RM20 8 \"User name: \" \"\" \"\"");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		if (tcp.getLine().equals("RM20 B")) {
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			String nameline = tcp.getLine();
			if (nameline.contains("RM20 A ")) {
				nameline = nameline.replaceAll("RM20 A ", "").replaceAll("\"",
						"");
				name = nameline;
				return true;
			}
		}
		return false;
	}

	private int menu(String text[]) {
		String send = "";
		for (int i = 0; i < text.length && i < 6; i++) {
			send += "\"" + text[i] + "\"";
			if (text.length < (i - 1)) {
				send += " ";
			}
		}
		tcp.sendLine("RM30 " + send);
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		String line = tcp.getLine();
		System.out.println(line);
		if (line.contentEquals("RM30 B")) {
			// if (tcp.getLine().contentEquals("RM30 B")) {
			tcp.sendLine("RM39 1");
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			if (tcp.getLine().contentEquals("RM39 A")) {
				System.out.println("Waiting for more data from server.");
				while (!tcp.getLineAvalible()) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
					}
				}
				String type = tcp.getLine();
				System.out.println(type);
				if (type.contains("RM30 A ")) {
					System.out.println(type);
					type = type.replaceAll("RM30 A ", "");
					System.out.println(type);
					return Integer.parseInt(type);
				}
			} else
				return 0;
		} else
			return 0;
		return 0;
	}

	private void weigh(int raavare, double amount, double tolerence) {
		String raavareName;
		try {
			raavareName = raavareDAO.getRaavare(raavare).getRaavareNavn();
		} catch (DALException e) {
			raavareName = String.valueOf(raavare);
		}
		List<RaavareBatchDTO> batchList = null;
		try {
			batchList = raavareBatchDAO.getRaavareBatchList(raavare);
		} catch (DALException e) {
			dialog("Sorry we are out of " + raavareName, DIALOG_OK);
			return;
		}
		String list = "";
		for (int i = 0; i < batchList.size(); i++) {
			list = batchList.get(i).getRbId() + " ";
		}
		int batchnumber = 0;
		while (batchnumber < 1) {
			dialog("Enter a batchnumber from one of these: " + list, DIALOG_OK);
			batchnumber = getNumber("Enter batchnumber:");
		}

		dialog("Place the container on the weight and press OK.", DIALOG_OK);
		double tara = tara();
		double weight = 0;

		setDetlabar(amount, (amount * tolerence / 100),
				(amount * tolerence / 100));

		dialog("Add " + amount + " kg of " + raavareName
				+ ". Use the deltabar to verify that you"
				+ " are within the tolerence margin!", DIALOG_OK);

		String menu[] = new String[1];
		menu[0] = "Done";
		while (true) {
			menu(menu);
			weight = getWeight();
			double max = (amount + (amount * tolerence / 100));
			double min = (amount - (amount * tolerence / 100));

			System.err.println("WEI: " + weight);
			System.err.println("MAX: " + max);
			System.err.println("MIN: " + min);
			if (Double.compare(min, weight) <= 0 && Double.compare(max, weight) >= 0) {
				dialog("Weight outside accepteble range.", DIALOG_OK);
			} else {
				ProduktBatchKompDTO produktbatchkomponent = new ProduktBatchKompDTO(
						0, batchnumber, tara, weight, user.getOprId());
				try {
					produktBatchKompDAO
							.createProduktBatchKomp(produktbatchkomponent);
				} catch (DALException e) {
					e.printStackTrace();
					return;
				}
				break;
			}
		}
		removeDeltabar();

		return;
	}

	private double getWeight() {
		System.out.println("getWeight() ");
		String line = "";
		while (!line.contains("S S ")) {
			tcp.sendLine("S");
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			line = tcp.getLine();
		}
		line = line.replaceAll("S S *", "").replaceAll(" kg", "");
		return Double.parseDouble(line);
	}

	private void removeDeltabar() {
		tcp.sendLine("P120");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		tcp.getLine();
	}

	private void setDetlabar(double target, double plus, double minus) {
		while (true) {
			plus = (double) Math.round(plus * 1000) / 1000;
			minus = (double) Math.round(minus * 1000) / 1000;
			String send = "P121 " + target + " kg " + plus + " kg " + minus
					+ " kg";
			tcp.sendLine(send);
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			String line = tcp.getLine();
			if (line.contains("P121 A")) {
				break;
			}
		}
	}

	private double tara() {
		tcp.sendLine("T");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		String line = tcp.getLine();
		if (line.contains("T S ")) {
			line = line.replaceAll("T S *", "").replaceAll(" *kg.*", "");
			return Double.parseDouble(line);
		}
		return 0.0;
	}

	private boolean dialog(String question, int type) {
		String send = "RM49 ";
		switch (type) {
		case DIALOG_NO_BUTTONS:
			send += DIALOG_NO_BUTTONS + " ";
			break;
		case DIALOG_OK:
			send += DIALOG_OK + " ";
			break;
		case DIALOG_CANCEL:
			send += DIALOG_CANCEL + " ";
			break;
		case DIALOG_OK_CANCEL:
			send += DIALOG_OK_CANCEL + " ";
			break;
		default:
			return false;
		}
		question = question.replaceAll("\"", "");
		question = String.format("%-200s", question);
		tcp.sendLine(send + "\"" + question + "\"");
		while (!tcp.getLineAvalible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		String resp = tcp.getLine();
		if (resp.equals("RM49 B")) {
			while (!tcp.getLineAvalible()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			resp = tcp.getLine();
			if (resp.equals("RM49 A 1")) {
				return true;
			}
			return false;
		} else {
			return false;
		}
	}

}
